package org.force4spring.support;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;

import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaClass;
import org.apache.commons.beanutils.DynaProperty;

import com.sforce.soap.partner.ChildRelationship;
import com.sforce.soap.partner.DescribeSObjectResult;
import com.sforce.soap.partner.Field;

public class SObjectDynaClass implements DynaClass, Serializable {

	protected DescribeSObjectResult describeResult;

	protected Map<String, DynaProperty> properties;

	private DynaProperty[] props;

	public SObjectDynaClass(DescribeSObjectResult describeResult) {
		this.describeResult = describeResult;
		properties = new HashMap<String, DynaProperty>();
		for (int i = 0; i < describeResult.getFields().length; i++) {
			String type = describeResult.getFields(i).getType().getValue();
			String soapType = describeResult.getFields(i).getSoapType().getValue();
			String name = describeResult.getFields(i).getName().toLowerCase();
			DynaProperty prop = new DynaProperty(name, mapSoapType(soapType));
			if (type.equals("multipicklist")) {
				prop = new DynaProperty(name, List.class, String.class);
			}

			properties.put(prop.getName(), prop);
			if (type.equals("reference")) {
				String relName = describeResult.getFields(i).getRelationshipName();
				DynaProperty relProp = new DynaProperty(relName.toLowerCase(), SObjectDynaBean.class);
				properties.put(relProp.getName(), relProp);
			}
		}

		// Process relationships
		if (describeResult.getChildRelationships() != null) {
			for (int i = 0; i < describeResult.getChildRelationships().length; i++) {
				ChildRelationship rel = describeResult.getChildRelationships(i);
				String name = rel.getRelationshipName();
				if (name != null) {
					name = name.toLowerCase();
					// Some standard relationships don't have name. Skip them.
					DynaProperty prop = new DynaProperty(name, List.class);
					properties.put(prop.getName(), prop);
				}
			}
		}

		props = properties.values().toArray(
				new DynaProperty[properties.values().size()]);
	}

	private Class mapSoapType(String fieldType) {
		if (fieldType.equals("tns:ID")) {
			return String.class;
		} else if (fieldType.equals("xsd:base64Binary")) {
			return Byte[].class;
		} else if (fieldType.equals("xsd:boolean")) {
			return Boolean.class;
		} else if (fieldType.equals("xsd:byte")) {
			return Byte.TYPE;
		} else if (fieldType.equals("xsd:date")) {
			return Date.class;
		} else if (fieldType.equals("xsd:dateTime")) {
			return Calendar.class;
		} else if (fieldType.equals("xsd:decimal")) {
			return BigDecimal.class;
		} else if (fieldType.equals("xsd:double")) {
			return Double.TYPE;
		} else if (fieldType.equals("xsd:float")) {
			return Float.TYPE;
		} else if (fieldType.equals("xsd:hexBinary")) {
			return Byte[].class;
		} else if (fieldType.equals("xsd:int")) {
			return Integer.TYPE;
		} else if (fieldType.equals("xsd:integer")) {
			return BigInteger.class;
		} else if (fieldType.equals("xsd:long")) {
			return Long.TYPE;
		} else if (fieldType.equals("xsd:QName")) {
			return QName.class;
		} else if (fieldType.equals("xsd:short")) {
			return Short.TYPE;
		} else if (fieldType.equals("xsd:string")) {
			return String.class;
		} else {
			throw new IllegalArgumentException("Unrecognized SOAP type: "
					+ fieldType);
		}
	}

	/**
	 * Returns value of 'describeResult' property
	 * 
	 * @return describeResult property
	 */
	public DescribeSObjectResult getDescribeResult() {
		return describeResult;
	}

	/**
	 * @see org.apache.commons.beanutils.DynaClass#getDynaProperties()
	 */
	public DynaProperty[] getDynaProperties() {
		return props;
	}

	/**
	 * @see org.apache.commons.beanutils.DynaClass#getDynaProperty(java.lang.String)
	 */
	public DynaProperty getDynaProperty(String name) {
		return properties.get(name);
	}

	/**
	 * @see org.apache.commons.beanutils.DynaClass#getName()
	 */
	public String getName() {
		return describeResult.getName();
	}

	/**
	 * @see org.apache.commons.beanutils.DynaClass#newInstance()
	 */
	public DynaBean newInstance() throws IllegalAccessException,
			InstantiationException {
		return new SObjectDynaBean(this);
	}
	
	public Field getField(String property) {
		Field[] fields = describeResult.getFields();
		for (int i=0;i<fields.length;i++) {
			if (property.equalsIgnoreCase(fields[i].getName())) {
				return fields[i];
			}
			
			if (property.equalsIgnoreCase(fields[i].getRelationshipName())) {
				return fields[i];
			}
		}
		return null;
	}
	
}
